package com.seewo.dubbo.debugger.admin.service.impl;

import com.alibaba.dubbo.common.URL;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.seewo.dubbo.debugger.admin.common.Constants;
import com.seewo.dubbo.debugger.admin.common.ServiceDiscovery;
import com.seewo.dubbo.debugger.admin.entity.Interface;
import com.seewo.dubbo.debugger.admin.entity.Service;
import com.seewo.dubbo.debugger.admin.exception.DubboDebuggerException;
import com.seewo.dubbo.debugger.admin.mapper.ServiceMapper;
import com.seewo.dubbo.debugger.admin.po.ServiceRegistryPO;
import com.seewo.dubbo.debugger.admin.service.IInterfaceService;
import com.seewo.dubbo.debugger.admin.service.IRegistryService;
import com.seewo.dubbo.debugger.admin.service.IServiceService;
import com.seewo.dubbo.debugger.admin.utils.EhcacheTemplate;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.*;


/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author chenliangliang
 * @since 2018-07-25
 */
@Slf4j
@org.springframework.stereotype.Service
public class ServiceServiceImpl extends ServiceImpl<ServiceMapper, Service> implements IServiceService {

    @Autowired
    private EhcacheTemplate ehcacheTemplate;

    @Autowired
    private ThreadPoolTaskExecutor executor;

    @Autowired
    private IRegistryService registryService;

    @Autowired
    private IInterfaceService interfaceService;

    @Override
    public ServiceMapper getMapper() {
        return this.baseMapper;
    }


    @Override
    public Service getInterfaceList(Service service) {
        final String address = service.getCAddress();
        final String protocol = service.getCProtocol();
        final Integer registryId = service.getCRegistryId();
        String key = registryId + address + protocol;
        ServiceRegistryPO serviceRegistryPo = getMapper().selectServiceRegistry(registryId, address, protocol);
        if (serviceRegistryPo == null) {
            throw new DubboDebuggerException("this service-" + service + " not exist");
        }
        //缓存5分钟数据
        return ehcacheTemplate.cacheable(Constants.CACHE_NAME_INTERFACE_LIST, key, () -> getInterfaceList0(service, serviceRegistryPo));
    }


    private Service getInterfaceList0(Service service, ServiceRegistryPO serviceRegistryPo) {
        final Integer serviceId = serviceRegistryPo.getServiceId();
        ServiceDiscovery serviceDiscovery = registryService.getServiceDiscovery(serviceRegistryPo.getRegistryProtocol(), serviceRegistryPo.getRegistryAddress());
        String serviceName = serviceRegistryPo.getServiceName();
        @SuppressWarnings("这里可能会发生异常：连接超时")
        List<List<URL>> serviceProviders = serviceDiscovery.getEnableServiceProviders();
        List<Interface> interfaces = new ArrayList<>();
        Set<String> nameSet = new HashSet<>();
        boolean canDebug = false;
        for (List<URL> urls : serviceProviders) {
            for (URL url : urls) {
                String prot = url.getProtocol();
                String addr = url.getAddress();
                String application = url.getParameter("application");
                if (StringUtils.equals(service.getCAddress(), addr) && StringUtils.equals(application, serviceName) && StringUtils.equals(service.getCProtocol(), prot)) {
                    String serviceInterface = url.getServiceInterface();
                    if (Constants.DUBBO_DEBUGGER_SERVICE.equals(serviceInterface)) {
                        canDebug = true;
                        continue;
                    }
                    nameSet.add(serviceInterface);
                    Interface inface = new Interface();
                    inface.setCName(serviceInterface);
                    inface.setCUrl(url.toString());
                    inface.setCServiceId(serviceId);
                    inface.setCVersion(url.getParameter("version"));
                    interfaces.add(inface);
                }
            }
        }
        service.setCId(serviceId);
        service.setCDesc(serviceRegistryPo.getServiceDesc());
        service.setCName(serviceName);
        service.setInterfaces(interfaces);

        if (interfaces.isEmpty()) {
            return service;
        }

        List<Interface> interfaceList = interfaceService.selectList(new EntityWrapper<Interface>()
                .eq("c_service_id", serviceId)
                .in("c_name", nameSet));
        Map<Interface, Interface> interfaceMap = new HashMap<>(64);
        interfaceList.forEach(i -> interfaceMap.put(i, i));
        List<Interface> insertList = new ArrayList<>();
        for (Interface iface : interfaces) {
            if (interfaceMap.containsKey(iface)) {
                Interface anInterface = interfaceMap.get(iface);
                iface.setCDesc(anInterface.getCDesc());
            } else {
                iface.setCCreateTime(new Date());
                iface.setCUpdateTime(new Date());
                insertList.add(iface);
            }
        }
        //异步更新interface表
        executor.execute(() -> updateTableInterface(insertList));
        //异步更新service表
        final boolean debug = canDebug;
        executor.execute(() -> {
            if (debug) {
                Service s = new Service();
                s.setCId(serviceId);
                s.setCDebug(1);
                updateById(s);
            }
        });
        return service;
    }

    private void updateTableInterface(List<Interface> insertList) {
        int size = insertList.size();
        if (size > 0) {
            int res = interfaceService.getMapper().batchInsert(insertList);
            if (res != size) {
                log.warn("批量插入interface失败: 需要插入：" + size + "条，实际插入：" + res + "条");
            }
        }
    }


    @Override
    public void updateDesc(Service service) {
        //异步更新缓存策略：先更新数据库，再删除缓存
        boolean res = updateById(service);
        if (res) {
            executor.execute(() -> {
                Service ser = selectById(service.getCId());
                String key = ser.getCId() + ser.getCAddress() + ser.getCProtocol();
                ehcacheTemplate.cacheEvict(Constants.CACHE_NAME_INTERFACE_LIST, key);
            });
        } else {
            throw new DubboDebuggerException("更新描述失败");
        }
    }
}
